Imports BikiSoft.Comm.Comm_Bikirom
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Imports System.Threading

Namespace Comm
    Friend Class Comm_Bikirom
        Shared m_lngHandle As Integer = 0

    End Class

    Friend Class Comm_Frame
        Public Enum BikiFrameType As Byte
            ID_CONSULT_PASS = &H91
            ID_CONSULT_INTEGRATED = &H92
            ID_FLASH_DATA = &H80
            ID_EEPROM_DATA = &H82
            ID_GENERAL_SPECIFIC = &H55
            ID_LAST_COMMAND_STATUS = &HAA
            ID_RUN_COMMAND = &HE0
            ID_BOOTLOADER = &H70
            ID_ATMEL = &H2A
            ID_PC = &H1A
            ID_ECU = &HA
        End Enum
        Public Enum BikiFrameResponse As Byte
            CMD_OK = &H1
            CMD_FAILED = &H2
            CMD_CRC_FAILED = &H3
            CMD_NEXT = &H4
            SIG_FAILED = &H5
            BUFFER_FULL = &H6
            CMD_INCOMPLETE = &H7
            CMD_UNKNOWN_SENDER = &H8
            CMD_UNKNOWN_RECIVER = &H9
            CMD_UNKNOWN = &H10
            CMD_BAD_HEADS = &HA
            CMD_TIMEOUT = &H1E
            CMD_BOOT = &H60
            SEC_BAD_CODE = &H30
            SEC_FUNCTION_LOCKED = &H31
            FLASH_WRITE_TIMEOUT = &H1F
            SYNC_FAILED = &HFF
            BOOT_FAILED = &H99
            CMD_PASSED = &HD
            CMD_NOTPASSED = &HF
        End Enum
        Public Enum BikiFrameAction As Byte
            WRITE = &H55
            READ = &HAA
            RUN_ERASEALL = &H80
            RUN_ERASEBANK1 = &H81
            RUN_ERASEBANK2 = &H82
            RUN_ERASESECTOR = &H88
            CMD_STOP = &HB
            CMD_GO = &HC
            RUN_SEC_UNLOCKCODE = &H90
            RUN_SEC_SETPASSWORD = &H92
            RUN_SEC_SETSECLEVEL = &H94
            RUN_GET_INFO = &H70
            RUN_DIAG_RXTX = &H60
            RUN_DIAG_RXCLK = &H61
            RUN_DIAG_FLASH = &H62
            RUN_DIAG_FLASH2 = &H63
            RUN_TEST_WRITE_PATTERN = &H64
            RUN_TEST_READ_PATTERN = &H65
            RUN_GET_PROG_ADC = &H66
            RUN_GET_BOARD_ADC = &H67
            DUT_ENABLE = &H68
            DUT_DISABLE = &H69
            RUN_TEST_OUTPUTS = &H6A
        End Enum
        Private Enum WaitValue As Integer
            WV_TIMEOUT = 258
            WV_LASTCMD = 0
            WV_CRCFAIL = 1
            WV_HEADERFAIL = 2
            WV_SPECIFIC = 3
        End Enum

        Dim m_Frame As Byte() = Nothing
        Dim m_Action As BikiFrameAction = Nothing
        Dim m_Response As BikiFrameType = Nothing

        Sub New(ByVal fType As BikiFrameType, Optional ByVal Address As UInt32 = 0, Optional ByVal Action As BikiFrameAction = Nothing, Optional ByVal Payload As Byte() = Nothing, Optional ByVal Response As BikiFrameType = BikiFrameType.ID_LAST_COMMAND_STATUS)
            Dim arrCmd() As Byte = Nothing
            Select Case fType
                Case BikiFrameType.ID_FLASH_DATA, BikiFrameType.ID_EEPROM_DATA
                    If Action = BikiFrameAction.WRITE Then
                        ReDim arrCmd(UBound(Payload) + 17)
                        Array.Copy(Payload, 0, arrCmd, 13, Payload.Length)
                    ElseIf Action = BikiFrameAction.READ Then
                        ReDim arrCmd(16)
                    End If
                    If Not IsNothing(arrCmd) Then
                        arrCmd(6) = fType
                        arrCmd(7) = Action
                        arrCmd(8) = (Address \ &H10000) And &HFF
                        arrCmd(9) = (Address \ &H100) And &HFF
                        arrCmd(10) = Address And &HFF
                        arrCmd(11) = (Payload.Length \ &H100) And &HFF
                        arrCmd(12) = Payload.Length And &HFF
                        frameHeadFootCrc(arrCmd)
                    End If
                Case BikiFrameType.ID_RUN_COMMAND
                    Select Case Action
                        Case BikiFrameAction.RUN_ERASESECTOR
                            ReDim arrCmd(14)
                            arrCmd(8) = (Address \ &H10000) And &HFF
                            arrCmd(9) = (Address \ &H100) And &HFF
                            arrCmd(10) = Address And &HFF
                        Case BikiFrameAction.RUN_SEC_SETPASSWORD, BikiFrameAction.RUN_SEC_SETSECLEVEL, BikiFrameAction.RUN_SEC_UNLOCKCODE
                            If IsNothing(Payload) Then
                                Throw New Exception("Communication Payload expected")
                            Else
                                ReDim arrCmd(11 + Payload.Length)
                                Array.Copy(Payload, 0, arrCmd, 8, Payload.Length)
                            End If
                        Case Else
                            ReDim arrCmd(11)
                    End Select
                    If Not IsNothing(arrCmd) Then
                        arrCmd(6) = fType
                        arrCmd(7) = Action
                        frameHeadFootCrc(arrCmd)
                    End If
                Case BikiFrameType.ID_CONSULT_INTEGRATED, BikiFrameType.ID_CONSULT_PASS
                    Select Case Action
                        Case BikiFrameAction.CMD_GO, BikiFrameAction.CMD_STOP
                            ReDim arrCmd(11)
                            arrCmd(6) = fType
                            arrCmd(7) = Action
                    End Select
                    frameHeadFootCrc(arrCmd)
                Case BikiFrameType.ID_BOOTLOADER
                    If IsNothing(Payload) Then
                        ReDim arrCmd(9)
                        frameHeadFootCrc(arrCmd)
                    Else
                        ReDim arrCmd(UBound(Payload) + 10)
                        Array.Copy(Payload, 0, arrCmd, 6, Payload.Length)
                        frameHeadFootCrc(arrCmd)
                    End If
                    arrCmd(2) = Comm.Comm_Frame.BikiFrameType.ID_BOOTLOADER
                Case BikiFrameType.ID_ECU
                    If Not IsNothing(Payload) AndAlso Payload.Length >= 2 Then
                        ReDim arrCmd(UBound(Payload) + 10)
                        Array.Copy(Payload, 0, arrCmd, 6, Payload.Length)
                        frameHeadFootCrc(arrCmd)
                        arrCmd(2) = Comm.Comm_Frame.BikiFrameType.ID_ECU
                    End If
            End Select
            m_Action = Action
            m_Frame = arrCmd
            m_Response = Response
        End Sub
        Sub New(ByVal Data() As Byte)
            m_Frame = Data
        End Sub
        Public Function IsValid() As Boolean
            Return crcValidate(m_Frame)
        End Function

        Public Property Data() As Byte()
            Get
                Return m_Frame
            End Get
            Set(ByVal value As Byte())
                m_Frame = value
            End Set
        End Property
        Public Property ID() As BikiFrameType
            Get
                If Not IsNothing(m_Frame) AndAlso m_Frame.Length > 7 Then
                    Return m_Frame(7)
                Else
                    Return Nothing
                End If
            End Get
            Set(ByVal value As BikiFrameType)
                m_Frame(2) = value
            End Set
        End Property

        ''' <summary>
        ''' Returns the payload part of the frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PayLoad(Optional ByVal WithID As Boolean = True) As Byte()
            Get
                Dim f_array() As Byte
                If WithID Then
                    ReDim f_array(m_Frame.Length - 11)
                    Array.Copy(m_Frame, 6, f_array, 0, m_Frame.Length - 10)
                Else
                    ReDim f_array(m_Frame.Length - 12)
                    Array.Copy(m_Frame, 7, f_array, 0, m_Frame.Length - 11)
                End If
                Return f_array
            End Get
        End Property

        Default Public Property Item(ByVal Index As UInt16) As Byte
            Get
                If Index < m_Frame.Length Then
                    Return m_Frame(Index)
                Else
                    Throw New IndexOutOfRangeException("Array Index Out of Bounds")
                End If
            End Get
            Set(ByVal value As Byte)
                m_Frame(Index) = value
            End Set
        End Property

        ''' <summary>
        ''' Sends the frame off, and passes back the array of the response if nothing than Last Command status. will throw any exceptions generated also. they need to be cought
        ''' </summary>
        ''' <param name="oArray">array to output the return to</param>
        ''' <param name="clearBuffers"></param>
        ''' <param name="RetryNum">Number of retries to allow for both timeout, crc and bad headers</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Function Send(Optional ByRef oArray As Byte() = Nothing, Optional ByVal clearBuffers As Boolean = True, Optional ByVal RetryNum As Integer = 1) As BikiFrameResponse
            Dim f_SetTxEvent As Boolean = False
            Dim ret As BikiFrameResponse = BikiFrameResponse.CMD_OK
            Try
                ' check to see if its actualy OK to send shit. dont want two threads sending commands at once
                If WaitHandle.WaitAll(New WaitHandle() {m_UsbTxEvent}, 500, False) = WaitHandle.WaitTimeout Then
                    Throw New USB_exceptions(FT_return.FT_OTHER_ERROR)
                Else
                    'm_UsbRxEvent2.Set()
                    m_UsbTxEvent2.Reset()
                    f_SetTxEvent = True
                    Dim f_wait As WaitValue
                    Dim f_Waits(2) As WaitHandle
                    f_Waits(0) = m_UsbLastCmdStatusEvent
                    f_Waits(1) = m_UsbCRCFailedEvent
                    f_Waits(2) = m_UsbHeadersFailedEvent

                    If clearBuffers Then
                        ' Reset the events before we stop on them!
                        m_UsbLastCmdStatusEvent.Reset()
                        m_UsbCRCFailedEvent.Reset()
                        m_UsbHeadersFailedEvent.Reset()

                        ' Clear out any buffers we need to..
                        Select Case m_Response
                            Case BikiFrameType.ID_EEPROM_DATA
                                m_UsbEEpromDataEvent.Reset()
                                Queue.Synchronized(m_UsbEEpromDataBuffer).Clear()
                            Case BikiFrameType.ID_FLASH_DATA
                                m_UsbFlashDataEvent.Reset()
                                Queue.Synchronized(m_UsbFlashDataBuffer).Clear()
                            Case BikiFrameType.ID_GENERAL_SPECIFIC
                                m_UsbGeneralSpecificEvent.Reset()
                                Queue.Synchronized(m_UsbGeneralSpecificBuffer).Clear()
                            Case BikiFrameType.ID_LAST_COMMAND_STATUS
                                m_UsbLastCmdStatusEvent.Reset()
                                Queue.Synchronized(m_UsbLastCmdStatusBuffer).Clear()
                            Case BikiFrameType.ID_CONSULT_PASS
                                m_UsbConsultPassBuffer = Nothing
                                m_UsbConsultEvent.Reset()
                        End Select
                    End If


                    If m_Response <> BikiFrameType.ID_LAST_COMMAND_STATUS Then
                        ReDim Preserve f_Waits(3)
                        Select Case m_Response
                            Case BikiFrameType.ID_EEPROM_DATA
                                f_Waits(3) = m_UsbEEpromDataEvent
                            Case BikiFrameType.ID_FLASH_DATA
                                f_Waits(3) = m_UsbFlashDataEvent
                            Case BikiFrameType.ID_GENERAL_SPECIFIC
                                f_Waits(3) = m_UsbGeneralSpecificEvent
                            Case BikiFrameType.ID_CONSULT_PASS
                                f_Waits(3) = m_UsbConsultEvent
                        End Select
                        If m_Response = BikiFrameType.ID_CONSULT_PASS Or _
                           m_Response = BikiFrameType.ID_CONSULT_INTEGRATED Then
                            ' Ignore bad heads for consult bypass mode. so many packets come thru errors
                            ' occur, acceptable errors. Ignore them
                            f_Waits(2) = New AutoResetEvent(False)
                        End If
                    End If

                    ' Now actualy SEND the Frame.. idiot!
                    If Not IsNothing(Me.Data) Then
                        Dim f_UsbExcept As New USB_exceptions(BikiRom_sendFrame(Me))
                    End If
                    m_UsbTxEvent2.Set()
                    m_UsbRxEvent.Set()

                    ' Now wait for the reply fool!
                    f_wait = WaitHandle.WaitAny(f_Waits, m_BikiInfo.TimeOut, True)
                    If f_wait <> WaitHandle.WaitTimeout Then
                        If f_wait = WaitValue.WV_CRCFAIL Then ' --------------- CRC Failed
                            'Console.WriteLine("COMM_FRAME: CRC Failed")
                            Return BK_return.BK_IO_CRC_FAILED
                            'Throw New Comm_Exceptions(BikiFrameResponse.CMD_CRC_FAILED, BikiFrameType.ID_PC)
                        ElseIf f_wait = WaitValue.WV_HEADERFAIL Then ' -------- Headers Failed
                            'Console.WriteLine("COMM_FRAME: HEAD FAIL")
                            Throw New Comm_Exceptions(BikiFrameResponse.CMD_BAD_HEADS, BikiFrameType.ID_PC)
                        ElseIf f_wait = WaitValue.WV_LASTCMD Then ' ----------- Last Command Status
                            ' Console.WriteLine("COMM_FRAME: Last Cmd")
                            Dim f_Frame As Comm_Frame = Queue.Synchronized(m_UsbLastCmdStatusBuffer).Dequeue()
                            If Not IsNothing(f_Frame) Then
                                Dim f_Except As New Comm_Exceptions(f_Frame.ID, f_Frame.Data(3))
                                ret = f_Frame.ID
                            End If
                        Else ' ------------------------------------------------ Specific Return request
                            ' do the stuff here
                            ' return the optional array reference here...

                            Dim f_Frame As Comm_Frame = Nothing
                            Select Case m_Response
                                Case BikiFrameType.ID_EEPROM_DATA
                                    'Console.WriteLine("COMM_FRAME: EEPROM Else")
                                    f_Frame = Queue.Synchronized(m_UsbEEpromDataBuffer).Dequeue
                                Case BikiFrameType.ID_FLASH_DATA
                                    'Console.WriteLine("COMM_FRAME: FLASH Else")
                                    f_Frame = Queue.Synchronized(m_UsbFlashDataBuffer).Dequeue
                                Case BikiFrameType.ID_GENERAL_SPECIFIC
                                    'Console.WriteLine("COMM_FRAME: GENERAL Else")
                                    f_Frame = Queue.Synchronized(m_UsbGeneralSpecificBuffer).Dequeue
                                Case BikiFrameType.ID_CONSULT_PASS
                                    ' Console.WriteLine("COMM_FRAME: CONSULT Else")
                                    If IsNothing(m_UsbConsultPassBuffer) Then
                                        oArray = Nothing
                                    Else
                                        oArray = m_UsbConsultPassBuffer.Clone 'ArrayList.Synchronized(m_UsbConsultPassBuffer).ToArray(GetType(Byte))
                                    End If
                            End Select
                            If Not IsNothing(f_Frame) Then oArray = f_Frame.PayLoad(False)
                        End If
                    Else
                        Throw New USB_exceptions(FT_return.FT_IO_TIMEOUT)
                    End If
                    End If
            Catch exUsb As USB_exceptions
                If f_SetTxEvent Then m_UsbTxEvent.Set()
                m_UsbTxEvent2.Set()
                m_UsbRxEvent.Set()
                If exUsb.Value = FT_return.FT_IO_TIMEOUT And RetryNum > 0 Then
                    Thread.Sleep(10)
                    Me.Send(oArray, True, RetryNum - 1)
                Else
                    Throw exUsb
                End If
                'If exUsb.Value <> FT_return.FT_OK Then m_BikiInfo.Initialised = False
            Catch exComm As Comm_Exceptions
                ' Auto Retry this crap again if retry num is greater than zero
                If (exComm.Response = BikiFrameResponse.CMD_BAD_HEADS Or _
                   exComm.Response = BikiFrameResponse.CMD_CRC_FAILED Or _
                   exComm.Response = BikiFrameResponse.CMD_TIMEOUT) And RetryNum > 0 _
                Then
                    m_UsbTxEvent2.Set()
                    If f_SetTxEvent Then m_UsbTxEvent.Set()
                    Thread.Sleep(m_BikiInfo.TimeOut)
                    Return Me.Send(oArray, True, RetryNum - 1)
                ElseIf exComm.Response <> BikiFrameResponse.CMD_OK Then
                    m_UsbTxEvent2.Set()
                    If f_SetTxEvent Then m_UsbTxEvent.Set()
                    Throw exComm
                    Return BikiFrameResponse.CMD_FAILED
                Else
                    m_UsbTxEvent2.Set()
                    If f_SetTxEvent Then m_UsbTxEvent.Set()
                    Return BikiFrameResponse.CMD_OK
                End If
            End Try
            m_UsbTxEvent2.Set()
            If f_SetTxEvent Then m_UsbTxEvent.Set()
            Return ret
        End Function

        Private Sub frameHeadFootCrc(ByRef frame As Byte())
            If Not IsNothing(frame) AndAlso frame.Length > 6 Then
                frame(0) = &HAA
                frame(1) = &HA5
                frame(2) = &H2A
                frame(3) = &H1A
                frame(4) = ((frame.Length - 10) \ &H100) And &HFF
                frame(5) = (frame.Length - 10) And &HFF
                frame(UBound(frame) - 1) = &H55
                frame(UBound(frame)) = &H5A
                crcCalculate(frame)
            End If
        End Sub

        ''' <summary>
        ''' This function Calculates the CRC of a BikiUSB packet and places it in the CRC location of the frame.
        ''' </summary>
        ''' <param name="Buffer"></param>
        ''' <remarks></remarks>
        Private Sub crcCalculate(ByRef Buffer As Byte())
            Dim i As Integer
            Dim cr As UShort = 0
            For i = 6 To UBound(Buffer) - 4
                cr = crc(Buffer(i), cr)
            Next
            Buffer(UBound(Buffer) - 3) = (cr \ &H100) And &HFF
            Buffer(UBound(Buffer) - 2) = cr And &HFF
        End Sub
        ''' <summary>
        ''' This function Validates the CRC of a BikiUSB packet
        ''' </summary>
        ''' <param name="Buffer"></param>
        ''' <returns>true is a valid packet</returns>
        ''' <remarks></remarks>
        Private Function crcValidate(ByRef Buffer As Byte()) As Boolean
            Dim i As Integer
            Dim cr As UShort = 0
            For i = 6 To UBound(Buffer) - 4
                cr = crc(Buffer(i), cr)
            Next
            If Buffer(UBound(Buffer) - 3) = ((cr \ &H100) And &HFF) And _
               Buffer(UBound(Buffer) - 2) = (cr And &HFF) _
            Then
                Return True
            Else
                Return False
            End If
        End Function

        Public Shared Function crc(ByVal cd As Byte, ByRef oldCrc As UShort) As UShort
            Dim i As Integer

            oldCrc = oldCrc Xor cd

            For i = 0 To 7
                If (oldCrc And 1) Then
                    oldCrc = (oldCrc \ 2) Xor &HA001
                Else
                    oldCrc = (oldCrc \ 2)
                End If
            Next

            Return oldCrc
        End Function

    End Class
End Namespace